package cn.sc.summer.token.encrypt;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.sc.summer.token.builder.TokenInfoBuilder;
import cn.sc.summer.token.po.Token;
import cn.sc.summer.token.util.AESUtil;
import cn.sc.summer.token.util.UserUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import cn.sc.summer.constant.token.TokenConstant;
import cn.sc.summer.redis.util.RedisHelper;
import cn.sc.summer.token.builder.TokenBuilderDirector;
import cn.sc.summer.token.enums.LoginTypeEnum;
import cn.sc.summer.token.enums.RequestTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * 类名：token工具类
 *
 * @author a-xin
 * @date 2023/9/5 11:28
 */
@Slf4j
@Component
public class TokenUtil {

    /**
     * 生成api访问唯一key值
     *
     * @param num             key的长度
     * @param requestTypeEnum 请求方式
     * @param serverName      服务名
     * @return 访问的apikey
     */
    public static String getApiEncryptKey(Integer num, String serverName, RequestTypeEnum requestTypeEnum) {
        String randomKey = RandomUtil.randomString(num);
        RedisHelper.set(requestTypeEnum.getKeyPrefix() + randomKey, serverName, 20 * 1000, TimeUnit.MILLISECONDS);
        return randomKey;
    }

    /**
     * 解析apikey是否合法
     *
     * @param apiKey       携带的apikey
     * @param requestTypeEnum 请求方式
     * @return api对应的服务名
     */
    public static String getApiEncryptKey(String apiKey, RequestTypeEnum requestTypeEnum) {
        if (!RedisHelper.hasKey(requestTypeEnum.getKeyPrefix() + apiKey)) {
            return "";
        } else {
            String serverName = RedisHelper.get(requestTypeEnum.getKeyPrefix() + apiKey).toString();
            RedisHelper.del(requestTypeEnum.getKeyPrefix() + apiKey);
            return serverName;
        }
    }

    /**
     * 解析apikey是否合法
     *
     * @param randomKey       携带的apikey
     * @param requestTypeEnum 请求方式
     */
    public static Boolean analysisApiEncryptKey(String randomKey, RequestTypeEnum requestTypeEnum) {
        if (!RedisHelper.hasKey(requestTypeEnum.getKeyPrefix() + randomKey)) {
            return false;
        } else {
            RedisHelper.del(requestTypeEnum.getKeyPrefix() + randomKey);
            return true;
        }
    }

    /**
     * 生成简易token信息
     *
     * @param userId 用户ID
     * @return token
     */
    public static String createSimpleToken(String userId, LoginTypeEnum loginTypeEnum) {
        byte[] bytes = (userId + RandomUtil.randomString(16)).getBytes(StandardCharsets.UTF_8);
        return DesUtil.encrypt(Base64.getEncoder().encodeToString(bytes)) + loginTypeEnum.getSlogan();
    }

    /**
     * 解析简易token信息
     *
     * @param token 用户token
     * @return 用户信息
     */
    public static String analysisSimpleToken(String token) {
        token = getRealToken(token);
        byte[] bytes = Base64.getDecoder().decode(DesUtil.decrypt(token));
        String userId = new String(bytes, StandardCharsets.UTF_8);
        return userId.substring(0, userId.length() - 16);
    }

    /**
     * 生成校验token信息
     *
     * @return 校验token
     */
    public static String createCheckToken() {
        return RandomUtil.randomString(32);
    }

    /**
     * 创建token
     *
     * @param userId 用户ID
     * @return token信息
     */
    public static String createToken(String userId, LoginTypeEnum loginTypeEnum) {
        if (StrUtil.isBlank(userId)) {
            throw new RuntimeException("The user ID cannot be empty！");
        }
        Token tokenInfo = new TokenBuilderDirector(new TokenInfoBuilder()).build();
        Token token = tokenInfo.setUserId(userId);
        String encrypt = EncryptUtil.encrypt(JSON.toJSONString(token));
        String randomKey = RandomUtil.randomString(16);
        String encryptKey = AESUtil.encrypt(randomKey, randomKey);
        return encryptKey + "." + encrypt + randomKey + loginTypeEnum.getSlogan();
    }

    /**
     * 解析token包含信息
     *
     * @param token token
     * @return 用户ID
     */
    public static String analysisToken(String token) {
        if (StrUtil.isBlank(token)) {
            throw new RuntimeException("The token that needs to be parsed cannot be empty！");
        }
        try {
            token = getRealToken(token);
            String[] split = token.split("\\.", 2);
            if (split.length > 1) {
                String randomKey = split[1].substring(split[1].length() - 16);
                if (randomKey.equals(AESUtil.decrypt(split[0], randomKey))) {
                    String encryptUserInfo = split[1].substring(0, split[1].length() - 16);
                    String decrypt = EncryptUtil.decrypt(encryptUserInfo);
                    return JSONObject.parseObject(decrypt).get("userId").toString();
                }
                throw new RuntimeException("Invalid token！");
            }
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
        throw new RuntimeException("Invalid token！");
    }

    /**
     * 删除token信息
     *
     * @param token token信息
     */
    public static void deleteSimpleToken(String token) {
        LoginTypeEnum loginType = getLoginType(token);
        String userId = analysisSimpleToken(token);
        RedisHelper.del(TokenConstant.ACCESS_TOKEN_PREFIX + token);
        RedisHelper.zRemoveMember(loginType.getSlogan() + userId, token);
    }

    /**
     * 删除token信息
     *
     * @param token token信息
     */
    public static void deleteToken(String token) {
        LoginTypeEnum loginType = getLoginType(token);
        String realToken = getRealToken(token);
        String userId = analysisToken(realToken);
        RedisHelper.del(TokenConstant.ACCESS_TOKEN_PREFIX + token);
        RedisHelper.zRemoveMember(loginType.getSlogan() + userId, token);
    }

    /**
     * 根据用户ID删除用户之前所存在的所有token信息
     *
     * @param userId 用户ID
     */
    public static void deleteUserToken(String userId) {
        Set<ZSetOperations.TypedTuple<Object>> typedTuples = new HashSet<>();
        for (LoginTypeEnum enums : LoginTypeEnum.values()) {
            String userToken = enums.getSlogan() + userId;
            if (RedisHelper.hasKey(userToken)) {
                Set<ZSetOperations.TypedTuple<Object>> userTokenSet = RedisHelper.zGetAllDES(userToken);
                if (CollUtil.isNotEmpty(userTokenSet)) {
                    typedTuples.addAll(userTokenSet);
                }
                RedisHelper.del(userToken);
            }
        }
        if (CollUtil.isNotEmpty(typedTuples)) {
            typedTuples.stream().forEach(typedTuple -> RedisHelper.del(TokenConstant.ACCESS_TOKEN_PREFIX + typedTuple.getValue()));
        }
    }

    /**
     * 获取token的真是解析信息
     *
     * @param originalToken 原始token信息
     * @return 真实token
     */
    protected static String getRealToken(String originalToken) {
        LoginTypeEnum loginType = getLoginType(originalToken);
        String slogan = loginType.getSlogan();
        return originalToken.substring(0, originalToken.length() - slogan.length());
    }

    /**
     * 获取登录方式
     *
     * @param token token信息
     * @return 登录方式
     */
    protected static LoginTypeEnum getLoginType(String token) {
        return LoginTypeEnum.getBySlogan(Objects.requireNonNull(UserUtil.getLoginUser(token)).getLoginType());
    }

}
